/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.repository;

import cz.mzk.oai.metadataRepository.tools.CannotReadDirectoryException;
import cz.mzk.oai.metadataRepository.metadata.MetadataFormat;
import cz.mzk.oai.metadataRepository.tools.FileTools;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.validation.SchemaFactory;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;

/**
 *
 * @author Martin Rehanek, Martin.Rehanek@gmail.com
 * http://www.edankert.com/validate.html
 */
public class MetadataValidator {

    //private static MetadataValidator instance;
    private static final Map<MetadataFormat, SAXReader> readers = new HashMap<MetadataFormat, SAXReader>(3);
    Set<File> allFiles = new HashSet<File>();
    Set<File> invalidFiles = new HashSet<File>();

    static {
        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
        SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        for (MetadataFormat format : MetadataFormat.values()) {
            try {
                parserFactory.setSchema(schemaFactory.newSchema(format.getSchemaUrl()));
                SAXParser parser = parserFactory.newSAXParser();
                readers.put(format, new SAXReader(parser.getXMLReader()));
            } catch (ParserConfigurationException ex) {
                Logger.getLogger(MetadataValidator.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SAXException ex) {
                Logger.getLogger(MetadataValidator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    private final MetadataFormat format;
    private final File dir;

    public MetadataValidator(File dir, MetadataFormat format) {
        this.format = format;
        this.dir = dir;
    }

    /*public static MetadataValidator instanceOf() {
    if (instance == null) {
    instance = new MetadataValidator();
    }
    return instance;
    }*/
    /**
     * @param metadataFormat
     * @return SAXReader that validates documents according to the schema of metadataFormat
     */
    public static SAXReader getValidatingSAXReader(MetadataFormat metadataFormat) {
        return readers.get(metadataFormat);
    }

    /**
     * Validates file using external schema obtained from format object. Interlan schema present in the document is ignored
     * @param file file to validate
     * @param format
     * @param printErrors
     * @return true if valid 
     */
    private boolean isValid(File file, MetadataFormat format, boolean printErrors) {
        SAXReader reader = readers.get(format);
        try {
            reader.read(file);
        } catch (DocumentException ex) {
            if (printErrors) {
                System.out.println(ex.getMessage());
            }
            return false;
        }
        return true;
    }

    /**
     *
     * @param file
     * @param format
     * @return null if file is valid, error message otherwise
     */
    private String getValidationError(File file, MetadataFormat format) {
        SAXReader reader = readers.get(format);
        try {
            reader.read(file);
        } catch (DocumentException ex) {
            return ex.getMessage();
        }
        return null;
    }

    /**
     * Tries to parse all files in directory and prints out their total number and number of those,
     * that are not valid according to format
     * @param dir
     * @param format
     * @param printNamesOfInvalidFiles
     * @throws CannotReadDirectoryException if the files in directory could not be read
     */
    public void validate(boolean printNamesOfInvalidFiles) throws CannotReadDirectoryException {
        List<String> filenames = FileTools.readFilenames(dir);
        System.out.println("Validating files in " + dir.getAbsolutePath());
        System.out.println("Xml schema: " + format.getSchemaUrl().toString());
        for (String filename : filenames) {
            File xmlFile = new File(dir.getAbsoluteFile() + File.separator + filename);
            if (xmlFile.isFile()) {//ignore directories
                /*try {
                Thread.sleep(1);
                } catch (InterruptedException ex) {
                Logger.getLogger(MetadataValidator.class.getName()).log(Level.SEVERE, null, ex);
                }*/
                allFiles.add(xmlFile);
                String errorMessage = getValidationError(xmlFile, format);
                if (errorMessage != null) {//i.e. invalid
                    invalidFiles.add(xmlFile);
                    if (printNamesOfInvalidFiles) {
                        //1.vypis souboru s chybama
                        System.out.println(xmlFile.getAbsolutePath());
                        System.out.println(errorMessage);

                        //2.vypis jednotlivych sysno
//                        String sysnoWithSuffix = filename.split("-")[1];
//                        String sysno = sysnoWithSuffix.substring(0, sysnoWithSuffix.length() - 4);
//                        System.out.println(sysno);
                    }
                }
            }
            /*if (allFiles.size() % 100 == 0) {
            System.out.println("So far processed files: " + allFiles.size());
            System.out.println("So far invalid: " + invalidFiles.size());
            }*/
        }
    }

    public Set<File> getAllFiles() {
        return allFiles;
    }

    public Set<File> getInvalidFiles() {
        return invalidFiles;
    }

    public void printCounters() {
        System.out.println("Number of files: " + allFiles.size());
        System.out.println("Number of invalid: " + invalidFiles.size());
    }
}
